// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证（版本 2.0）进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动！任何基于本项目二次开发而产生的一切法律纠纷和责任，我们不承担任何责任！

using Admin.NET.Application.Service.PlanViews.Dto;
using Admin.NET.Core;

namespace Admin.NET.Application.Service.PlanViews;

/// <summary>
/// 统计卡片服务实现
/// 负责生成首页的4个统计卡片数据：实际开工、实际完工、延期开工、延期完工
/// </summary>
public class StatisticsCardService : IStatisticsCardService, ITransient
{
    private readonly IDataFilterService _dataFilterService;

    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="dataFilterService">数据筛选服务</param>
    public StatisticsCardService(IDataFilterService dataFilterService)
    {
        _dataFilterService = dataFilterService;
    }

    /// <summary>
    /// 获取统计卡片完整数据
    /// 包含4个卡片数据和时间进度，用于StatisticsCards.vue组件
    /// </summary>
    /// <param name="filter">筛选条件</param>
    /// <param name="cancellationToken">取消令牌</param>
    /// <returns>统计卡片完整响应数据</returns>
    public async Task<StatisticsCardsResponse> GetCardsDataAsync(BusinessMonthFilter filter, CancellationToken cancellationToken = default)
    {
        try
        {
            // 获取筛选后的数据
            var planData = await _dataFilterService.GetFilteredDataAsync(filter, cancellationToken);
            var (startDate, endDate) = _dataFilterService.GetDateRange(filter);

            // 并行计算所有卡片数据
            var cardTasks = new[]
            {
                Task.Run(() => CalculateStartCard(planData, startDate, endDate), cancellationToken),
                Task.Run(() => CalculateEndCard(planData, startDate, endDate), cancellationToken),
                Task.Run(() => CalculateDelayStartCard(planData, startDate, endDate), cancellationToken),
                Task.Run(() => CalculateDelayEndCard(planData, startDate, endDate), cancellationToken)
            };

            var cards = await Task.WhenAll(cardTasks);

            // 计算时间进度
            var totalDays = (endDate - startDate).Days + 1;
            // 确定"当前日期"：如果是历史数据则用业务月结束日期，否则用今天
            var currentDate = DateTime.Now.Date > endDate.Date ? endDate.Date : DateTime.Now.Date;
            var passedDays = Math.Min((currentDate - startDate.Date).Days + 1, totalDays);
            var timeProgressRate = totalDays > 0 ? Math.Round((decimal)passedDays / totalDays * 100, 2) : 0;

            return new StatisticsCardsResponse
            {
                Cards = cards.ToList(),
                TimeProgressRate = Math.Max(0, timeProgressRate),
                PassedDays = Math.Max(0, passedDays),
                TotalDays = totalDays,
                LoadTime = DateTime.Now,
                CacheExpiry = DateTime.Now.AddMinutes(2) // 2分钟缓存
            };
        }
        catch (Exception ex)
        {
            // 记录错误日志（这里可以使用日志框架）
            throw new Exception($"计算统计卡片数据失败: {ex.Message}", ex);
        }
    }


    /// <summary>
    /// 计算实际开工卡片数据
    /// 统计计划开工日期在业务月内的项目开工情况
    /// </summary>
    /// <param name="planData">计划数据</param>
    /// <param name="startDate">业务月开始日期</param>
    /// <param name="endDate">业务月结束日期</param>
    /// <returns>开工卡片数据</returns>
    private CardItem CalculateStartCard(List<PlanDataCore> planData, DateTime startDate, DateTime endDate)
    {
        // 筛选计划开工日期在业务月内的项目
        var planStartInMonth = planData.Where(p =>
            p.PlanStart.HasValue &&
            p.PlanStart.Value.Date >= startDate.Date &&
            p.PlanStart.Value.Date <= endDate.Date).ToList();

        var planCount = planStartInMonth.Count;
        var actualCount = planStartInMonth.Count(p => p.ActualStart.HasValue);
        var rate = CalculateRate(actualCount, planCount);

        return new CardItem
        {
            CardType = "ActualStart",
            Title = "实际开工",
            Subtitle = $"计划开工 {planCount} 项",
            PlanCount = planCount,
            ActualCount = actualCount,
            Rate = rate,
            IconType = "start",
            Color = GetCardColor(rate)
        };
    }

    /// <summary>
    /// 计算实际完工卡片数据
    /// 统计计划完工日期在业务月内的项目完工情况
    /// </summary>
    /// <param name="planData">计划数据</param>
    /// <param name="startDate">业务月开始日期</param>
    /// <param name="endDate">业务月结束日期</param>
    /// <returns>完工卡片数据</returns>
    private CardItem CalculateEndCard(List<PlanDataCore> planData, DateTime startDate, DateTime endDate)
    {
        // 筛选计划完工日期在业务月内的项目
        var planEndInMonth = planData.Where(p =>
            p.PlanEnd.HasValue &&
            p.PlanEnd.Value.Date >= startDate.Date &&
            p.PlanEnd.Value.Date <= endDate.Date).ToList();

        var planCount = planEndInMonth.Count;
        var actualCount = planEndInMonth.Count(p => p.ActualEnd.HasValue);
        var rate = CalculateRate(actualCount, planCount);

        return new CardItem
        {
            CardType = "ActualEnd",
            Title = "实际完工",
            Subtitle = $"计划完工 {planCount} 项",
            PlanCount = planCount,
            ActualCount = actualCount,
            Rate = rate,
            IconType = "end",
            Color = GetCardColor(rate)
        };
    }

    /// <summary>
    /// 计算延期开工卡片数据
    /// 统计计划开工但未按时开工的项目
    /// </summary>
    /// <param name="planData">计划数据</param>
    /// <param name="startDate">业务月开始日期</param>
    /// <param name="endDate">业务月结束日期</param>
    /// <returns>延期开工卡片数据</returns>
    private CardItem CalculateDelayStartCard(List<PlanDataCore> planData, DateTime startDate, DateTime endDate)
    {
        // 确定"当前日期"：如果是历史数据则用业务月结束日期，否则用今天
        var currentDate = DateTime.Now.Date > endDate.Date ? endDate.Date : DateTime.Now.Date;

        // 在业务月期间内（从开始日期到当前日期之前）应该开工的项目
        var shouldStartInPeriod = planData.Where(p =>
            p.PlanStart.HasValue &&
            p.PlanStart.Value.Date >= startDate.Date &&
            p.PlanStart.Value.Date < currentDate).ToList();

        // 在此期间内计划开工但未实际开工的项目（延期开工）
        var delayedStartItems = shouldStartInPeriod.Where(p => !p.ActualStart.HasValue).ToList();

        var totalShouldStart = shouldStartInPeriod.Count;
        var delayStartCount = delayedStartItems.Count;
        // 计算延期率
        var delayRate = totalShouldStart > 0 ? (decimal)delayStartCount / totalShouldStart * 100 : 0;
        // 显示按时开工率（100% - 延期率）
        var onTimeRate = Math.Max(0, 100 - delayRate);
        var displayRate = Math.Round(onTimeRate, 2);

        return new CardItem
        {
            CardType = "DelayStart",
            Title = "延期开工",
            Subtitle = $"应开工 {totalShouldStart} 项",
            PlanCount = totalShouldStart,
            ActualCount = delayStartCount,
            Rate = displayRate, // 显示按时开工率
            IconType = "delay-start",
            Color = GetDelayCardColor(delayRate) // 颜色基于延期率
        };
    }

    /// <summary>
    /// 计算延期完工卡片数据
    /// 统计计划完工但未按时完工的项目
    /// </summary>
    /// <param name="planData">计划数据</param>
    /// <param name="startDate">业务月开始日期</param>
    /// <param name="endDate">业务月结束日期</param>
    /// <returns>延期完工卡片数据</returns>
    private CardItem CalculateDelayEndCard(List<PlanDataCore> planData, DateTime startDate, DateTime endDate)
    {
        // 确定"当前日期"：如果是历史数据则用业务月结束日期，否则用今天
        var currentDate = DateTime.Now.Date > endDate.Date ? endDate.Date : DateTime.Now.Date;

        // 在业务月期间内（从开始日期到当前日期之前）应该完工的项目
        var shouldEndInPeriod = planData.Where(p =>
            p.PlanEnd.HasValue &&
            p.PlanEnd.Value.Date >= startDate.Date &&
            p.PlanEnd.Value.Date < currentDate).ToList();

        // 在此期间内计划完工但未实际完工的项目（延期完工）
        var delayedEndItems = shouldEndInPeriod.Where(p => !p.ActualEnd.HasValue).ToList();

        var totalShouldEnd = shouldEndInPeriod.Count;
        var delayEndCount = delayedEndItems.Count;
        // 计算延期率
        var delayRate = totalShouldEnd > 0 ? (decimal)delayEndCount / totalShouldEnd * 100 : 0;
        // 显示按时完工率（100% - 延期率）
        var onTimeRate = Math.Max(0, 100 - delayRate);
        var displayRate = Math.Round(onTimeRate, 2);

        return new CardItem
        {
            CardType = "DelayEnd",
            Title = "延期完工",
            Subtitle = $"应完工 {totalShouldEnd} 项",
            PlanCount = totalShouldEnd,
            ActualCount = delayEndCount,
            Rate = displayRate, // 显示按时完工率
            IconType = "delay-end",
            Color = GetDelayCardColor(delayRate) // 颜色基于延期率
        };
    }

    /// <summary>
    /// 计算完成率
    /// </summary>
    /// <param name="actual">实际完成数</param>
    /// <param name="plan">计划总数</param>
    /// <returns>完成率百分比</returns>
    private decimal CalculateRate(int actual, int plan)
    {
        return plan > 0 ? Math.Round((decimal)actual / plan * 100, 2) : 0;
    }

    /// <summary>
    /// 根据完成率获取卡片颜色
    /// </summary>
    /// <param name="rate">完成率</param>
    /// <returns>颜色标识</returns>
    private string GetCardColor(decimal rate)
    {
        return rate switch
        {
            >= 90 => "green",    // 优秀
            >= 70 => "blue",     // 良好  
            >= 50 => "orange",   // 一般
            _ => "red"           // 较差
        };
    }

    /// <summary>
    /// 根据延期率获取卡片颜色（延期率越高颜色越严重）
    /// </summary>
    /// <param name="rate">延期率</param>
    /// <returns>颜色标识</returns>
    private string GetDelayCardColor(decimal rate)
    {
        return rate switch
        {
            0 => "green",        // 无延期
            <= 10 => "blue",     // 轻微延期
            <= 30 => "orange",   // 中等延期  
            _ => "red"           // 严重延期
        };
    }
}
